#include "tcp_server.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include "socket_tool.h"
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>

static void sigchld_handler(int sig) {
    int status;
    pid_t pid;
    // 不阻塞，使用WNOHANG循环处理所有已终止的子进程
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        if (WIFEXITED(status)) {
            printf("子进程 %d 正常退出，状态: %d\n", pid, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("子进程 %d 被信号终止: %d\n", pid, WTERMSIG(status));
        }
    }
    return;
}

/*
 * 自定义信号处理函数Signal
 */
typedef void Sigfunc(int);
static Sigfunc* Signal(int sig, Sigfunc *func) {
    struct sigaction act, oact;
    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(sig, &act, &oact) < 0)
        return SIG_ERR;
    return oact.sa_handler;
}

void start_server()
{
    puts("启动服务器");

    // 创建监听套接字
    int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    struct sockaddr_in server_ipv4;
    server_ipv4.sin_family = AF_INET;
    server_ipv4.sin_port = htons(12345);
    inet_pton(AF_INET, "127.0.0.1", &server_ipv4.sin_addr.s_addr);
    bind(listen_fd, (struct sockaddr*) &server_ipv4, sizeof(server_ipv4));

    listen(listen_fd, 5);

    // 设置SIGCHLD信号处理
    /*
     * 注意：在大多数传统的 Unix 系统（如 Linux）上，signal() 函数的行为是：
     * 当信号处理器被调用一次后，内核会自动将该信号的处理方式重置为默认行为（SIG_DFL）
     * 因此只有第一个终止进程的终止信号能被sigchld_handler函数处理，其他进程的终止信号默认行为已被重置为默认行为（SIG_DFL），不处理此终止信号
     */
    // if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) {
    //     perror("signal");
    //     return;
    // }

    // 使用自定义的signal函数
    if (Signal(SIGCHLD, sigchld_handler) == SIG_ERR) {
        perror("signal");
        return;
    }

    while (true)
    {
        struct sockaddr_in client_addr;
        size_t client_len = sizeof(client_addr);
        // 连接套接字
        puts("等待连接...");
        int sockfd = accept(listen_fd, &client_addr, &client_len);
        if (sockfd < 0)
        {
            if (errno == EINTR) {
                // 服务器子进程被关闭后，接收到终止信号会导致此调用被中断，因此这里回头继续accept
                puts("accept被中断，重新调用");
                continue;
            } else {
                puts("accept失败");
                exit(-1);
            }
        }
        
        char *client = sockaddr_to_string(&client_addr);
        printf("连接成功，client=%s\n", client);
        free(client);
        // 创建子进程
        pid_t pid = fork();
        if (pid < 0) {
            // 创建子进程失败
            close(sockfd);
        } else if (pid == 0) {
            // 子进程
            // 子进程关闭监听套接字
            close(listen_fd);
            while (true) {
                String *msg = read_tcp_line(sockfd);
                if (msg == NULL) {
                    puts("关闭连接");
                    close(sockfd);
                    exit(0);
                }
                printf("接收到客户端的消息：%s\n", msg->start);
                if (strcmp("bye", msg->start) == 0) {
                    Destroy_String(msg);
                    puts("关闭连接");
                    close(sockfd);
                    exit(0);
                }
                Destroy_String(msg);
                // 给客户端发消息
                char buf[256];
                memset(buf, 0, sizeof(buf));
                printf("发送消息：");
                fgets(buf, sizeof(buf), stdin);
                write(sockfd, buf, strlen(buf) + 1);
                if (strcmp("bye\n", buf) == 0) {
                    puts("关闭连接");
                    close(sockfd);
                    exit(0);
                }
            }
        } else {
            // 父进程
            printf("创建子进程成功，pid=%d\n", pid);
            // 父进程关闭连接套接字
            close(sockfd);
        }

    }
    

}